#ifndef SERVERASYNCREQUEST_H_
#define SERVERASYNCREQUEST_H_

#include "scatu/ServerRequests.h"

namespace scatu {

template<TServerFunction Function, typename ReturnType, typename ArgsType>
class RAsynchronousRequest<EServer, Function, ReturnType, ArgsType> : public RServerRequestBase
    {
    friend class RServerRequestArray;
    
private:
    
    typedef typename ReturnType::ReturnType IntegralReturnType;
    
    // Needed for run time defined functions.
    struct Callable
        {
        virtual void CallL(ArgsType const & aArgs, MFunctor<IntegralReturnType> & aRetFunc) = 0;
        };
    
    // void return for serve function
    template<typename ServerType>
    class TServeFunction : public Callable
        {
    public:
        typedef typename ArgsType::template MemFunWithArgs<ServerType, IntegralReturnType> MemFun;
        typedef typename MemFun::VoidFuncPtr FuncPtr;
        
        TServeFunction()
          : iServer(NULL), iServeFunction(NULL) {}
        TServeFunction(ServerType & aServer, FuncPtr aFuncPtr)
          : iServer(&aServer), iServeFunction(aFuncPtr) {}
        
        void CallL(ArgsType const & aArgs, MFunctor<IntegralReturnType> & aRetFunc)
            { return MemFun::CallL(*iServer, iServeFunction, aArgs, aRetFunc); }
        
    private:
        ServerType * iServer;
        FuncPtr iServeFunction;
        };

public:
    RAsynchronousRequest()
      : RServerRequestBase(Function)
      , iServerFunction(NULL)
      , iReturnBuffer(NULL)
      , iReturnFunctor(*this)
        {}
    
    template<typename ServerType>
    void SetServeFunctionL(ServerType & aServer, typename TServeFunction<ServerType>::FuncPtr aFuncPtr)
        {
        delete iServerFunction;
        iServerFunction = NULL;
        iServerFunction = new (ELeave) TServeFunction<ServerType>(aServer, aFuncPtr);
        }
    
    void UnregisterServeFunctionL() { delete iServerFunction; }
    
protected: // RRequestBase implementation
    void Close()
        {
        delete iReturnBuffer;
        iReturnBuffer = NULL;
        delete iServerFunction;
        iServerFunction = NULL;
        }
    
    virtual TBool IsPending() { return !iMessage.IsNull(); }
    virtual TBool ReadyToReturn() { return iReturnBuffer != NULL; }
    
    virtual void ReceiveDataL(RMessage2 const & aMessage)
        {
        // Make sure no data is left over from previous calls
        delete iReturnBuffer;
        iReturnBuffer = NULL;
        iMessage = aMessage;
        
        // Read arguments
        HBufC8 * argBuf = ReadArgumentsLC(aMessage);
        ArgsType argumentData;
        CleanupClosePushL(argumentData);
        argumentData.DeserializeL(*argBuf);
        
        iServerFunction->CallL(argumentData, iReturnFunctor);
        
        CleanupStack::PopAndDestroy(2, argBuf);
        }
    
    virtual void ReturnDataL(RMessage2 const & aMessage)
        {
        aMessage.WriteL(EMessageSlotData, *iReturnBuffer);
        aMessage.Complete(KErrNone);
        
        delete iReturnBuffer;
        iReturnBuffer = NULL;
        }
    
private:
    class ReturnFunctor : public MFunctor<IntegralReturnType>
        {
    public:
        ReturnFunctor(RAsynchronousRequest & aParent) : iParent(aParent) {}
    
        void operator()(IntegralReturnType aRet)
            {
            // TODO clean up
            ReturnType returnData(aRet, EReturnTypeDesignator);
            CleanupClosePushL(returnData);
            iParent.iReturnBuffer = returnData.SerializeL();
            CleanupStack::PopAndDestroy(&returnData);
            TPckg<TInt> sizeBuf(iParent.iReturnBuffer->Size());
            iParent.iMessage.WriteL(EMessageSlotBufSize, sizeBuf);
            iParent.iMessage.Complete(KErrNone);
            }
    private:
        RAsynchronousRequest & iParent;
        };
    
private:
    Callable * iServerFunction;
    HBufC8 * iReturnBuffer;
    RMessagePtr2 iMessage;
    ReturnFunctor iReturnFunctor;
    };


} // namespace scatu

#endif /* SERVERASYNCREQUEST_H_ */
